/*
虽然普通的文件流读取写入文件非常便捷，但每次都是从外部IO设备（比如硬盘，相对于内存来说是外部）读取写入
速度达不到内存的速度，很有可能造成程序反应迟钝，性能不够高。缓冲流能够提供一个缓冲，提前将部分内容写入内存，
下次读取时，如果缓冲区存在此数据，则无需请求外部设备。同理，当向外部设备写数据时，先写入到缓冲区，而不是直接写入到外部设备
缓冲流的工作原理，BufferedInputStream/BufferedOutputStream不直接操作数据源，而是对其他字节流的包装，他们是处理流。。
集合图片进行理解
*/
package javaio.BufferedInputOutputSteam;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class Main {
    public static void main(String[] args) {
        //要创建一个缓冲字节流，只需要把原本的FileInputStream作为构造参数传入BufferedInputStream即可
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"))){
            System.out.println((char) bufferedInputStream.read()); //使用起来和原来的文件字节流一样的
        }catch (IOException e){
            e.printStackTrace();
        }
        //实际上进行IO操作的并不是BufferedInputStream,而是我们传入的FileInputStream，而BufferedInputStream虽然有着同样的方式，
        // 实际上进行了一些额外的处理然后再调用FileInputStream的同名方法，这样的写法称为装饰者模式
        // 实际上这种模式是父类FilterInputStream提供的规范，后面还会降到更多FilterInputStream的子类
        // BufferedInputStream中还专门有一个用于缓冲的数组  protected volatile byte buf[];

        /*
        I/O操作一般不能重复读取内容，比如键盘发送的信号，主机接收了就没了，而缓冲流提供了缓冲机制，一部分内容可以被暂时保存，BufferedInputStream
        支持reset()和mark()操作
        mark()方法调用后，会标记当前读到的位置，标记之后如果后面再调用reset()方法，会使得读取位置回到当时标记的位置
        public synchronized void mark(int readlimit) {
            in.mark(readlimit);
        }
        当调用mark()后，输入流会以某种方式保留之后读取的readlimit数量的内容，当读取的内容超过readlimit之后，之后的内容不会被保留到缓冲区，mark位置也会失效。
        但这里要注意的是，缓冲区大小不仅仅由readlimit决定
        当调用reset()后，会使得当前的读取位置回到mark()调用时的位置
         */

        try(BufferedInputStream bufferedInputStream2 = new BufferedInputStream(new FileInputStream("test.txt"))){
            bufferedInputStream2.mark(1); //只保留之后的1个字节
            System.out.println((char) bufferedInputStream2.read());
            System.out.println((char) bufferedInputStream2.read());  //读两次，读两个字节
            bufferedInputStream2.reset();  //读取两个字节后，reset()回到mark时的位置
            System.out.println((char) bufferedInputStream2.read());  //查看reset()后，readlimit仅为1的情况下，mark() 的位置还在不
            System.out.println((char) bufferedInputStream2.read());
        }catch (IOException e){
            e.printStackTrace();
        }

        //执行结果为 a b a b，发现虽然设置缓冲区readlimit为1，但读取两个字节到缓冲区后，reset()还是能回到mark位置，为什么
        //其实mark之后保存的内容，是去readlimit和bufferedInputStream类的缓冲区大小两者中的最大值，而并非完全由readlimit确定。
        //我们可以限制下缓冲区大小，再观察结果
        try(BufferedInputStream bufferedInputStream3 = new BufferedInputStream(new FileInputStream("test.txt"),1)){  //设置缓冲区大小为1
            bufferedInputStream3.mark(1); //设置readlimit只保留之后的1个字节
            System.out.println((char) bufferedInputStream3.read());
            System.out.println((char) bufferedInputStream3.read());  //读两次，读两个字节
//            bufferedInputStream3.reset();  //读取两个字节后，reset()回到mark时的位置，会报错，因为mark已经失效，无法reset， Resetting to invalid mark
            System.out.println((char) bufferedInputStream3.read());
            System.out.println((char) bufferedInputStream3.read());
        }catch (IOException e){
            e.printStackTrace();
        }


        //了解完BufferedInputSteam后，再看BufferedOutputStream，其实和BufferedInputStream原理差不多，只是反向操作
        try(BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("output.txt"))){
            bufferedOutputStream.write("lbwnb".getBytes());
            bufferedOutputStream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }

        /*
        缓冲字符流和缓冲字节流一样，也有一个专门的缓冲区，BufferedReader构造时需要传入一个Reader对象,BufferedReader内部也包含了一个缓存数组
        private char cb[];
         */
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader("test.txt"))){
            System.out.println(bufferedReader.readLine()); //按行读取
            System.out.println((char) bufferedReader.read()); //读下一行的一个字符
            //缓冲字符流同样支持mark()和reset()操作
            bufferedReader.mark(1);
            System.out.println((char) bufferedReader.read());
            System.out.println((char) bufferedReader.read());
            bufferedReader.reset();
            System.out.println((char) bufferedReader.read());
            System.out.println((char) bufferedReader.read());


        }catch (IOException e){
            e.printStackTrace();
        }

        //BufferedWriter在处理纯文本文件时同样方便
        try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("output.txt"))){
            bufferedWriter.newLine();  //使用newLine()进行换行
            bufferedWriter.write("汉堡做的行不行");  //可以直接写入一个字符串
            bufferedWriter.flush(); // 强制写入，并清空缓冲区
        }catch (IOException e){
            e.printStackTrace();
        }




    }

}
